#include "RtspServerWorker.h"
#include <QThread>
#include <iostream>
#include "ffmpeg_pusher.h"

namespace jlh {
	RtspServerWorker::RtspServerWorker(QObject *parent)
		: QObject(parent)
	{
		connect(this, SIGNAL(sstart()), this, SLOT(ostart()));
		connect(this, SIGNAL(sstop()), this, SLOT(ostop()));
		connect(this, SIGNAL(sFrame()), this, SLOT(onFrame()));
	}

	RtspServerWorker::~RtspServerWorker()
	{
	}

	void RtspServerWorker::Start(const char* ip, uint16_t port)
	{
		ip_ = std::string(ip);
		port_ = port;

		emit sstart();
	}

	void RtspServerWorker::Stop()
	{
		emit sstop();
		QThread* p = thread();
		if (p)
		{
			p->wait();
		}
	}

	void RtspServerWorker::ostart()
	{
		rtsp_server_.reset(new QTcpServer(), [](QTcpServer* s) { s->deleteLater(); });
		connect(rtsp_server_.get(), SIGNAL(newConnection()), this, SLOT(onConnect()));
		bool b = rtsp_server_->listen(QHostAddress(QString::fromStdString(ip_)), port_);
		std::cout << "Server, " << ip_ << ":" << port_ << ", Start " << (b ? "Success" : "Failed") << std::endl;
	}

	void RtspServerWorker::ostop()
	{
		if (rtsp_server_)
		{
			if (rtsp_server_->isListening())
			{
				rtsp_server_->close();
			}

			{
				std::vector<std::shared_ptr<RtspConnection>> clients;
				{
					std::lock_guard<std::mutex> l(mutex_);
					rtsp_clients_.swap(clients);
				}
				for (auto client : clients)
				{
					client->DisConnect();
				}
			}

			rtsp_server_->disconnect(this);
			rtsp_server_.reset();
		}

		QThread* p = thread();
		if (p)
		{
			p->exit();
		}
		std::cout << "Server," << ip_ << ":" << port_ << ", Stopped " << std::endl;
	}

	void RtspServerWorker::onConnect()
	{
		if (!rtsp_server_ || !rtsp_server_->isListening())
			return;

		QTcpSocket* sock = rtsp_server_->nextPendingConnection();
		if (!sock)
		{
			return;
		}

		std::cout << "New Connection." << sock->peerAddress().toString().toStdString() << std::endl;
		auto client = std::make_shared<RtspConnection>(shared_from_this(), sock);
		{
			std::lock_guard<std::mutex> l(mutex_);
			rtsp_clients_.push_back(client);
		}
	}

	MediaSessionId RtspServerWorker::AddSession(MediaSession* session)
	{
		{
			std::lock_guard<std::mutex> l(mutex_);
			if (rtsp_suffix_map_.find(session->GetRtspUrlSuffix()) != rtsp_suffix_map_.end()) {
				return 0;
			}

			std::shared_ptr<MediaSession> media_session(session);
			MediaSessionId sessionId = media_session->GetMediaSessionId();
			rtsp_suffix_map_.emplace(std::move(media_session->GetRtspUrlSuffix()), sessionId);
			media_sessions_.emplace(sessionId, std::move(media_session));
			return sessionId;
		}
	}

	void RtspServerWorker::RemoveSession(MediaSessionId sessionId)
	{
		std::lock_guard<std::mutex> l(mutex_);
		auto itr = media_sessions_.find(sessionId);
		if (media_sessions_.end() != itr)
		{
			rtsp_suffix_map_.erase(itr->second->GetRtspUrlSuffix());
			media_sessions_.erase(sessionId);
		}
	}

	MediaSessionPtr RtspServerWorker::LookMediaSession(const std::string& suffix)
	{
		{
			std::lock_guard<std::mutex> l(mutex_);
			auto itr = rtsp_suffix_map_.find(suffix);
			if (rtsp_suffix_map_.end() != itr) {
				MediaSessionId id = itr->second;
				return media_sessions_[id];
			}
		}

		return nullptr;
	}

	MediaSessionPtr RtspServerWorker::LookMediaSession(MediaSessionId sessionId)
	{
		{
			std::lock_guard<std::mutex> l(mutex_);
			auto itr = media_sessions_.find(sessionId);
			if (media_sessions_.end() != itr)
				return itr->second;
		}
		return nullptr;
	}

	bool RtspServerWorker::PushFrame(MediaSessionId sessionId, MediaChannelId channelId, AVFrame& frame)
	{
		ServerFrame sf;
		sf.channelId = channelId;
		sf.sessionId = sessionId;
		sf.frame = frame;
		{
			std::lock_guard<std::mutex> l(mutex_);
			frames_.push_back(sf);
		}
		emit sFrame();
		return true;
	}

	void RtspServerWorker::onFrame()
	{
		std::list<ServerFrame> frames;
		{
			std::lock_guard<std::mutex> l(mutex_);
			frames_.swap(frames);
		}

		std::shared_ptr<MediaSession> session = nullptr;
		for (auto itr = frames.begin(); itr != frames.end(); ++itr)
		{
			{
				std::lock_guard<std::mutex> l(mutex_);
				auto its = media_sessions_.find(itr->sessionId);
				if (its == media_sessions_.end())
					continue;
				session = its->second;
			}
			if (0 < session->GetNumClient()) {
				session->HandleFrame(itr->channelId, itr->frame);
			}
		}
	}

}